﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Kinect;
using KinectNui = Microsoft.Kinect;

namespace Microsoft.Samples.Kinect.WpfViewers {
    public class KinectPedal : KinectMovement {
        [DllImport("user32.dll", EntryPoint = "keybd_event", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern void Keybd_event(byte vk, byte scan, int flags, int extrainfo);

        //const int AKEY = 65;
        const int AKEY = 87;
        const int BKEY = 66;
        const int CKEY = 67;
        //const int CKEY = 160;
        const int DKEY = 68;
        const int EKEY = 69;
        const int LShift = 160;

        const int SCANCODE = 0;
        const int KEYDOWN = 0;
        const int KEYUP = 2;


        //http://community.bistudio.com/wiki/DIK_KeyCodes
        const int DIK_A = 30;
        //const int DIK_A = 11;
        const int DIK_W = 11;
        const int DIK_LSHIFT = 42;
        //const int DIK_LSHIFT = 46;


        //What frame are we on, I have this so I can put in an algorithm and based upon how slow or fast someone is pedaling it will hit the speed accordingly
        //It will be a work in progress
        int frame = 0;

        float leftankle = float.MinValue;
        float rightankle = float.MinValue;

        public int slowpedal = 1200;
        public int mediumpedal = 850;
        public int highpedal = 650;
        public int boostpedal = 400;
        float diffinpedals = 0.10f;
        long pedalcount = 0;

        bool boostlast = true;

        bool together = false;
        int fullmotion = 0;

        Microsoft.Kinect.Skeleton skeleton = null;

        public bool AllowLean = true;
        public string Lean = "No Lean";
        public float LeanDeadZone = .08f;
        bool LeanLeft = false;
        bool LeanRight = false;

        public bool HeadLean = false;

        public bool AllowWheelies = true;
        public string Wheelie = "No Wheelie";
        public float WheelieDeadZone = .08f;
        bool LeanForward = false;
        bool LeanBackward = false;

        public bool AllowBrake = true;
        bool Brake = false;

        public bool AllowReverse = true;
        bool Reverse = false;

        InputStruct gas = new InputStruct();
        InputStruct boost = new InputStruct();

        InputStruct left = new InputStruct();
        InputStruct right = new InputStruct();

        InputStruct forward = new InputStruct();
        InputStruct backward = new InputStruct();

        InputStruct brake = new InputStruct();
        InputStruct reverse = new InputStruct();

        public string GasKey {
            get { return gas.key; }
        }

        public string BoostKey {
            get { return boost.key; }
        }

        public KinectPedal() {
            FakeInput.Init();
            gas = FakeInput.GetInputStruct("A");
            boost = FakeInput.GetInputStruct("LSHIFT");

            gas = FakeInput.GetInputStruct("W");
            boost = FakeInput.GetInputStruct("C");

            left = FakeInput.GetInputStruct("A");
            right = FakeInput.GetInputStruct("D");

            forward = FakeInput.GetInputStruct("UP");
            backward = FakeInput.GetInputStruct("DOWN");

            brake = FakeInput.GetInputStruct("Z");
            reverse = FakeInput.GetInputStruct("SPACE");
        }

        public void SwapControls()
        {
            InputStruct swap;
            swap = FakeInput.GetInputStruct(left.key);
            left = FakeInput.GetInputStruct(right.key);
            right = FakeInput.GetInputStruct(swap.key);

            swap = FakeInput.GetInputStruct(brake.key); ;
            brake = FakeInput.GetInputStruct(reverse.key);
            reverse = FakeInput.GetInputStruct(swap.key);
        }

        public override void SetKeys(string gaskey, string boostkey, string leanleft, string leanright, string _brake, string _reverse) {
            gas = FakeInput.GetInputStruct(gaskey);
            boost = FakeInput.GetInputStruct(boostkey);
            
            left = FakeInput.GetInputStruct(leanleft);
            right = FakeInput.GetInputStruct(leanright);
            brake = FakeInput.GetInputStruct(_brake);
            reverse = FakeInput.GetInputStruct(_reverse);

        }

        public override void UpdateSkeleton(Skeleton data) {

            leftankle = data.Joints[JointType.AnkleLeft].Position.Y;
            rightankle = data.Joints[JointType.AnkleRight].Position.Y;
            
            skeleton = data;
        }

        public override void CheckMovement() {
            if (!together && Math.Abs(leftankle - rightankle) < 0.10f){// && (DateTime.Now - start).TotalMilliseconds > 150) {
                if (!together && Math.Abs(leftankle - rightankle) < diffinpedals) {
                    fullmotion++;
                    together = true;
                    if (fullmotion == 2) {
                        steps++;
                        fullmotion = 0;
                        pedalcount++;
                        tp = DateTime.Now - start;
                        start = DateTime.Now;

                    }
                }
            } else if (together && Math.Abs(leftankle - rightankle) > diffinpedals) {
                together = false;
            }

            //Save last frames
            frame++;
            if (frame > 1) {
                frame = 0;
            }

            //Check to see if leaning side to side
            if (null != skeleton) {
                float center = skeleton.Joints[JointType.HipCenter].Position.X;
                float lean = skeleton.Joints[JointType.ShoulderCenter].Position.X;
                if (HeadLean) {
                    center = skeleton.Joints[JointType.ShoulderCenter].Position.X;
                    lean = skeleton.Joints[JointType.Head].Position.X;
                }
                if ((center - lean) > LeanDeadZone) {
                    LeanLeft = true;
                    LeanRight = false;
                    Lean = "Left";
                } else if ((center - lean) < (-1 * LeanDeadZone)) {
                    LeanRight = true;
                    LeanLeft = false;
                    Lean = "Right";
                } else {
                    LeanLeft = false;
                    LeanRight = false;
                    Lean = "No Lean";
                }
            }

            //Check to see if leaning front to back
            if (null != skeleton) {
                float center = skeleton.Joints[JointType.HipCenter].Position.Z;
                float lean = skeleton.Joints[JointType.ShoulderCenter].Position.Z;
                if ((center - lean) > WheelieDeadZone) {
                    LeanForward = true;
                    LeanBackward = false;
                    Wheelie = "Forward";
                } else if ((center - lean) < (-1 * WheelieDeadZone)) {
                    LeanBackward = true;
                    LeanForward = false;
                    Wheelie = "Backward";
                } else {
                    LeanBackward = false;
                    LeanForward = false;
                    Wheelie = "No Lean";
                }
            }

            if (null != skeleton) {
                if (skeleton.Joints[JointType.HandLeft].Position.Y > skeleton.Joints[JointType.Head].Position.Y) {
                    Brake = true;
                } else { Brake = false; }
                if (skeleton.Joints[JointType.HandRight].Position.Y > skeleton.Joints[JointType.Head].Position.Y) {
                    Reverse = true;
                } else { Reverse = false; }
            }


        }

        public override void MovePlayer() {
            if (!Active) {
                FakeInput.SimulateKeyUp(gas);
                FakeInput.SimulateKeyUp(boost);
                return;
            }

            //We see if the pedaling wasn't too fast or too slow.  Also have to make sure it hasn't been a long time since they last actually pedaled
            if (tp.TotalMilliseconds < slowpedal && tp.TotalMilliseconds > boostpedal && (DateTime.Now - start).TotalMilliseconds < slowpedal) {
                //We are pedaling, just not full pedal, so we hit button every other frame
                if (tp.TotalMilliseconds > mediumpedal) {
                    if (frame == 0) {
                        FakeInput.SimulateKeyDown(gas);
                        FakeInput.SimulateKeyUp(boost);
                    } else {
                        FakeInput.SimulateKeyUp(gas);
                        FakeInput.SimulateKeyUp(boost);
                    }
                } else {//We are at full pedal so make sure the Akey is down
                    FakeInput.SimulateKeyDown(gas);
                }
                HasGas = true;

                //Did they pedal fast do they want to go extra fast (at least in burnout paradise)
                if (tp.TotalMilliseconds < highpedal && tp.TotalMilliseconds > boostpedal) {
                    HasBoost = true;
                    if (boostlast) {
                        FakeInput.SimulateKeyDown(boost);
                    }
                    boostlast = true;
                } else {
                    FakeInput.SimulateKeyUp(boost);
                    HasBoost = false;
                    boostlast = false;
                }
            } else {//We are not pedaling so make sure no keys are pressed

                FakeInput.SimulateKeyUp(gas);
                FakeInput.SimulateKeyUp(boost);
                HasGas = false;
                HasBoost = false;
                boostlast = false;
            }

            //Lean
            if (LeanLeft) {
                FakeInput.SimulateKeyDown(left);
                FakeInput.SimulateKeyUp(right);
            } else if (LeanRight) {
                FakeInput.SimulateKeyDown(right);
                FakeInput.SimulateKeyUp(left);
            } else {
                FakeInput.SimulateKeyUp(left);
                FakeInput.SimulateKeyUp(right);
            }

            //Lean
            if (LeanForward) {
                FakeInput.SimulateKeyDown(forward);
                FakeInput.SimulateKeyUp(backward);
            } else if (LeanBackward) {
                FakeInput.SimulateKeyDown(backward);
                FakeInput.SimulateKeyUp(forward);
            } else {
                FakeInput.SimulateKeyUp(forward);
                FakeInput.SimulateKeyUp(backward);
            }

            if (Brake) {
                FakeInput.SimulateKeyDown(brake);
            } else {
                FakeInput.SimulateKeyUp(brake);
            }
            if (Reverse) {
                FakeInput.SimulateKeyDown(reverse);
            } else {
                FakeInput.SimulateKeyUp(reverse);
            }
            
        }

    }
}
